﻿using Engine;
using Engine.Graphics;
using Scenario_Editor.AddinManager;
using Scenario_Editor.Controls;
using Scenario_Editor.Tools;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
using WorldRacing2.Material;
using WorldRacing2.Terrain;

namespace WorldRacing2.WindowsFormsControls
{
    public partial class TerrainMaterialBrush : UserControl, ITerrainPaintingControl
    {
        #region Private Fields

        /// <summary>
        /// Reference to World Racing 2 addin.
        /// </summary>
        private readonly IAddinScenarioEditor addin;

        private MaterialAssignWindows materialAssignWindow;
        private MaterialManager materialManager;
        private Scenario_Editor.Tools.ITerrain terrain;

        #endregion Private Fields

        #region Public Constructors

        public TerrainMaterialBrush(Scenario_Editor.AddinManager.IAddinScenarioEditor addin)
        {
            InitializeComponent();
            this.materialAssignWindow = new MaterialAssignWindows();
            this.toolTip_SlopeVertexColor.SetToolTip(this.numericUpDown_PaintBlendColorMax,
                "Here you can set the maximal angle where the Vertexcolor could be painted.\n" +
                "Set it to \"90\" if you want to paint the complete terrain. \n"
                + "Set it to eg \"15\" to paint only flat terrains with a steep of maximum angle of 15 degrees.");

            this.toolTip_SlopeVertexColor.SetToolTip(this.numericUpDown_PaintBlendColorMin,
                "Here you can set the minimal angle where the Vertexcolor could be painted.\n" +
                "Set it to \"0\" if you want paint flat horizontal surface. \n"
                + "Set it to eg \"45\" to paint only steep terrains. ");

            this.materialPainter.ShowDarkenVertexColorButton = false;
            this.materialPainter.ShowLightenVertexColorButton = false;
            this.comboBox_MaterialMode.DataSource = Enum.GetValues(typeof(EnumMappingMode));
            this.comboBox_MaterialMode.SelectedIndex = 0;
            // this.currentMaterial = new Wr2Material("", "", "", EnumMappingMode.material00);//this.textureListBox.SelectedIndex, this.textureListBox.SelectedIndex, this.textureListBox.SelectedIndex,
            // this.materialPainter.CurrentMappingMode, this.materialManager);
            this.addin = addin;
        }

        #endregion Public Constructors

        #region Public Properties

        public ushort MaterialID
        {
            get
            {
                return (ushort)this.materialManager.GetMaterialIDFromGameMaterial(this.materialPainter.GetMaterial());
                //   return this.CurrentTerrainMaterial.GetCurrentMaterialID(this.materialManager);
            }
            set
            {
                //currentMaterial.PropertiesChanged += this.materialPainter.SetMaterial;
                throw new Exception("Is this needed?");
                this.materialPainter.SetMaterial(this.materialManager.GetMaterial(value), null);
            }
        }

        public bool PaintMaterial
        {
            get
            {
                return this.materialPainter.checkBox_Material.Checked;
            }

            set
            {
                this.materialPainter.checkBox_Material.Checked = value;
            }
        }

        public bool PaintVertexData
        {
            get
            {
                return this.materialPainter.checkBox_PaintVertexColor.Checked;
            }

            set
            {
                this.materialPainter.checkBox_PaintVertexColor.Checked = value;
            }
        }

        /// <summary>
        /// Texture Brush size is needed for scenry editor to show for width of circle for cursor is drawn.
        /// </summary>
        public float TextureBrushSize
        {
            get
            {
                return (float)this.trackBar_sizeTextureBrush.Value;
            }
            set
            {
                this.trackBar_sizeTextureBrush.Value = (decimal)value;
            }
        }

        public Engine.Graphics.IGameVertex VertexData
        {
            get
            {
                return this.materialPainter.Wr2Vertex;
            }

            set
            {
                throw new NotImplementedException("Set of Vertexcolor is not implemented in World Racing 2 materialPainter.");
            }
        }

        #endregion Public Properties

        #region Internal Properties

        internal Scenario_Editor.Tools.ITerrain Terrain
        {
            get { return terrain; }
            set { terrain = value; }
        }

        #endregion Internal Properties

        #region Public Methods

        public Engine.Graphics.Material GetCurrentTerrainMaterial()
        {
            return new Engine.Graphics.Material(this.materialPainter.GetMaterial());
        }

        /// <summary>
        /// Everytime a new materialmanager is set in scenario editor, this method is called.
        /// </summary>
        /// <param name="sender">the materialmanager</param>
        /// <param name="e"></param>
        public void MaterialManagerChanged(object sender, EventArgs e)
        {
            Debug.WriteLine("new MaterialManager for TerrainControl!");
            this.materialManager = (MaterialManager)sender;
            this.materialAssignWindow.SetMaterialManager(this.materialManager);
            this.materialPainter.SetDevice(this.materialManager);

            int number = 0;
            textureListBox.Items.Clear();
            foreach (string file in materialManager.TextureNames)
            {
                try
                {
                    textureListBox.Items.Add(System.IO.Path.GetFileName(file));
                    number++;
                }
                catch (Exception exception)
                {
                    MessageBox.Show(exception.ToString());
                    MessageBox.Show("Error: " + file);
                }
            }

            if (textureListBox.Items.Count > 0)
                textureListBox.SelectedIndex = 0;

            // Create  a bew standard Material.
            Wr2Material material = new Wr2Material(0, 0, 0, EnumMappingMode.material00, this.materialManager, this.materialPainter.TextureMatrixT1, this.materialPainter.TextureMatrixT2, this.materialPainter.TextureMatrixT3);
            this.materialPainter.SetMaterial(new Engine.Graphics.Material(material), null);
            // materialManager.AddNewMaterial(new Engine.Graphics.Material(material), false);
            // int materialID = materialManager.GetMaterialID(this.materialPainter.GetMaterial());
        }

        public void OnMaterialChange(object material, EventArgs e)
        {
            Engine.Graphics.Material material2 = (Engine.Graphics.Material)material;

            this.materialPainter.SetMaterial(material2, e);
        }

        void ITerrainPaintingControl.UseTool(Vector3 positionOnTerrain, Engine.Engine engine, float viewDistance, RoadV2 roadBuilder, ObjectPlacerControl objectPlacer)
        {
            var mouse = engine.Mouse;
            var keyboard = engine.Keyboard;

            Update_terrain_painting(engine, positionOnTerrain, viewDistance, objectPlacer);

            // calculates the slope of the terrain position under the mouse pointer.
            Ray ray = new Ray(new Vector3(positionOnTerrain.X, positionOnTerrain.Y + 100, positionOnTerrain.Z), Vector3.Down * 100);
            var coordinate = terrain.GetCellCoordinate(positionOnTerrain);
            float angle;
            if (Terrain.GetNormalFromCell(coordinate.X, coordinate.Y, ray, true, false, out angle))
            {
                SetSlopeUnderCursorText(angle);
            }
            else
                SetSlopeUnderCursorText(0);
        }

        #endregion Public Methods

        #region Internal Methods

        internal void OnTerrainChanged(object sender, EventArgs e)
        {
            this.terrain = (Scenario_Editor.Tools.Terrain.Terrain)sender;
        }

        #endregion Internal Methods

        #region Private Methods

        private void button2_Click(object sender, EventArgs e)
        {
            this.textBox_TextureSearchText.Text = string.Empty;
        }

        private void comboBox_MaterialMode_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.materialPainter.CurrentMappingMode = (EnumMappingMode)this.comboBox_MaterialMode.SelectedItem;
        }

        private void numericUpDown_PaintBlendColor_ValueChanged(object sender, EventArgs e)
        {
            if (sender == this.numericUpDown_PaintBlendColorMin)
                if (this.numericUpDown_PaintBlendColorMin.Value > this.numericUpDown_PaintBlendColorMax.Value)
                    this.numericUpDown_PaintBlendColorMax.Value = this.numericUpDown_PaintBlendColorMin.Value;
        }

        private void searchTextBox_TextChanged(object sender, EventArgs e)
        {
            this.textureListBox.Items.Clear();
            string searchString = this.textBox_TextureSearchText.Text.ToUpper();
            this.textureListBox.BeginUpdate();
            for (int i = 0; i < this.materialManager.TextureNames.Count; i++)
            {
                if (searchString == string.Empty || this.materialManager.TextureNames[i].ToUpper().Contains(searchString))
                    this.textureListBox.Items.Add(this.materialManager.TextureNames[i]);
            }
            this.textureListBox.EndUpdate();

            // shows the info label of searchbox.
            this.label_InfoTextTextureFilter.Visible = (searchString == string.Empty) ? true : false;
        }

        private void SetSlopeUnderCursorText(float value)
        {
            this.label_SlopeValue.Text = value.ToString("##.#°");
        }

        private void textureListBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.materialPainter.SetTexture(this.materialManager.GetTextureID((string)textureListBox.SelectedItem));
        }

        private void Update_terrain_painting(Engine.Engine engine, Vector3 cursorPosition, float viewDistance, ObjectPlacerControl sceneryObjectManager)
        {
            var mouse = engine.Mouse;
            var keyboard = engine.Keyboard;

            if (mouse.ButtonL)
            {
                if (checkBox_GetTextureFromTerrain.Checked == true)
                {
                    // Gets the material from the cell.
                    Vector3 intersectionObjects;
                    int materialIdObjects;
                    int materialIdTerrain = Terrain.GetMaterialID(cursorPosition);
                    sceneryObjectManager.objectManager.IntersectObject(engine.Grafic.VNear, engine.Grafic.VFar, viewDistance, out materialIdObjects, out intersectionObjects);

                    this.materialAssignWindow.SetCurrentMaterial(this.materialPainter.GetMaterial());

                    if (materialIdTerrain > -1 || materialIdObjects > -1)
                    {
                        if (intersectionObjects != Vector3.Zero
                            && GeoMath.DistanceBtw2Points(engine.Grafic.VNear, intersectionObjects) < GeoMath.DistanceBtw2Points(engine.Grafic.VNear, cursorPosition) + 1000.0f)
                        {
                            this.materialAssignWindow.SetPickedupMateiral((Wr2Material)this.materialManager.GetMaterial(materialIdObjects).GameMaterialProperties);
                        }
                        else
                        {
                            this.materialAssignWindow.SetPickedupMateiral((Wr2Material)this.materialManager.GetMaterial(materialIdTerrain).GameMaterialProperties);
                        }

                        if (this.materialAssignWindow.ShowDialog() == DialogResult.OK)
                        {
                            this.materialPainter.SetMaterial(this.materialAssignWindow.GetNewMaterial(), null);
                        }
                        return;
                    }
                }

                if (cursorPosition != Vector3.Zero)
                {
                    // Get The cells which should painted
                    List<System.Drawing.Point> cellsWhichWhichShouldPainted = Terrain.GetCellsUnderCursor(cursorPosition, trackBar_sizeTextureBrush.Value + (decimal)(terrain.SECTORSIZE1 / terrain.numberOfCellsPerSector));

                    //Paint Vertex Color
                    if ((this.materialPainter.PaintMode1 & MaterialPainter.PaintMode.PaintVertexColor) == MaterialPainter.PaintMode.PaintVertexColor)
                    {
                        TerrainTools.PaintVertexColor(terrain, this.addin, cursorPosition, cellsWhichWhichShouldPainted, ((MyOwnVertex.Wr2Data)this.materialPainter.Wr2Vertex),
                            Math.Max((decimal)(terrain.SECTORSIZE1 / terrain.numberOfCellsPerSector / 2.0f), trackBar_sizeTextureBrush.Value - (decimal)((float)trackBar_sizeTextureBrush.Value + terrain.SECTORSIZE1 / terrain.numberOfCellsPerSector) / 10),
                             (float)this.numericUpDown_PaintBlendColorMin.Value, (float)this.numericUpDown_PaintBlendColorMax.Value);
                    }
                    //Paint Material
                    if ((this.materialPainter.PaintMode1 & MaterialPainter.PaintMode.PaintMaterial) == MaterialPainter.PaintMode.PaintMaterial)
                    {
                        int materialID = this.materialManager.GetMaterialID(this.materialPainter.GetMaterial());
                        if (materialID == -1)
                            materialID = this.materialManager.AddNewMaterial(new Engine.Graphics.Material(this.materialPainter.GetMaterial()), true);

                        Terrain.PaintMaterialOnTerrain(cursorPosition, cellsWhichWhichShouldPainted, trackBar_sizeTextureBrush.Value, materialID, viewDistance);
                    }
                    // Smooth VertecColor
                    if ((this.materialPainter.PaintMode1 & MaterialPainter.PaintMode.SmoothVertexColor) == MaterialPainter.PaintMode.SmoothVertexColor)
                    {
                        TerrainTools.SmoothVertexColor(terrain, cursorPosition, cellsWhichWhichShouldPainted, (int)((float)trackBar_sizeTextureBrush.Value + terrain.SECTORSIZE1 / terrain.numberOfCellsPerSector), 10);
                    }
                    // Lighten Up VertecColor
                    if ((this.materialPainter.PaintMode1 & MaterialPainter.PaintMode.LightenVertexColor) == MaterialPainter.PaintMode.LightenVertexColor)
                    {
                        TerrainTools.LightenVertexColor(this.terrain, cursorPosition, cellsWhichWhichShouldPainted, trackBar_sizeTextureBrush.Value);
                    }
                    // Darken VertecColor
                    if ((this.materialPainter.PaintMode1 & MaterialPainter.PaintMode.DarkenVertexColor) == MaterialPainter.PaintMode.DarkenVertexColor)
                    {
                        TerrainTools.DarkenVertexColor(this.terrain, cursorPosition, cellsWhichWhichShouldPainted, trackBar_sizeTextureBrush.Value);
                    }
                    Terrain.UpdateTheCells(cellsWhichWhichShouldPainted);

                    //Terrain.UpdateChangedTerrain(null);
                }
            }
            else
            {
                if (mouse.ButtonLreleased)
                {
                    checkBox_GetTextureFromTerrain.Checked = false;
                }
            }
        }

        #endregion Private Methods
    }
}